﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace ifunction.Fiddler.Model
{
    /// <summary>
    /// Class FiddlerRequest.
    /// </summary>
    [DataContract]
    public class FiddlerRequest
    {
        #region Property

        /// <summary>
        /// Enum MethodType
        /// </summary>
        [DataContract]
        public enum MethodType
        {
            /// <summary>
            /// The get
            /// </summary>
            [EnumMember]
            GET = 0,
            /// <summary>
            /// The post
            /// </summary>
            [EnumMember]
            POST,
            /// <summary>
            /// The put
            /// </summary>
            [EnumMember]
            PUT,
            /// <summary>
            /// The delete
            /// </summary>
            [EnumMember]
            DELETE
        }

        /// <summary>
        /// Gets or sets the target URL.
        /// </summary>
        /// <value>The target URL.</value>
        [DataMember]
        public Uri TargetUri { get; set; }

        /// <summary>
        /// Gets or sets the referer URI.
        /// </summary>
        [DataMember]
        public Uri RefererUri { get; set; }

        /// <summary>
        /// Gets or sets the accept.
        /// </summary>
        /// <value>The accept.</value>
        [DataMember]
        public string Accept { get; set; }

        /// <summary>
        /// Gets or sets the user agent.
        /// </summary>
        /// <value>The user agent.</value>
        [DataMember]
        public string UserAgent { get; set; }

        /// <summary>
        /// Gets or sets the cookie string.
        /// </summary>
        /// <value>The cookie string.</value>
        [DataMember]
        public string CookieString { get; set; }

        /// <summary>
        /// Gets or sets the type of the content.
        /// </summary>
        /// <value>The type of the content.</value>
        [DataMember]
        public string ContentType { get; set; }

        /// <summary>
        /// Gets or sets the type of the method.
        /// </summary>
        /// <value>The type of the method.</value>
        [DataMember]
        public MethodType Method { get; set; }

        /// <summary>
        /// Gets or sets the cookie container.
        /// </summary>
        /// <value>The cookie container.</value>
        public CookieContainer CookieContainer { get; protected set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="FiddlerRequest" /> class.
        /// </summary>
        public FiddlerRequest()
            : this(null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FiddlerRequest"/> class.
        /// </summary>
        /// <param name="cookieContainer">The cookie container.</param>
        public FiddlerRequest(CookieContainer cookieContainer)
            : this(cookieContainer, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FiddlerRequest" /> class.
        /// </summary>
        /// <param name="fiddlerRequestRaw">The fiddler request raw.</param>
        public FiddlerRequest(string fiddlerRequestRaw)
            : this(null, fiddlerRequestRaw)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FiddlerRequest" /> class.
        /// </summary>
        /// <param name="cookieContainer">The cookie container.</param>
        /// <param name="fiddlerRequestRaw">The fiddler request raw.</param>
        protected FiddlerRequest(CookieContainer cookieContainer, string fiddlerRequestRaw)
        {
            this.CookieContainer = cookieContainer;

            if (this.CookieContainer == null)
            {
                this.CookieContainer = new CookieContainer();
            }
            InitializeByRaw(this, fiddlerRequestRaw);
        }

        #endregion

        /// <summary>
        /// Automatics the raw.
        /// </summary>
        /// <returns>System.String.</returns>
        public string ToRaw()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine(Method.ToString() + " " + this.TargetUri.ToString() + " HTTP 1.1");
            builder.AppendLine("Host: " + this.TargetUri.Host);
            builder.AppendLine("Connection: keep-alive");
            builder.AppendLine("Accept: " + (string.IsNullOrWhiteSpace(this.Accept) ? "*/*" : this.Accept));
            builder.AppendLine("User-Agent: " + this.UserAgent.GetStringValue());
            builder.AppendLine("DNT: 1");
            builder.AppendLine("Referer: " + (this.RefererUri == null ? string.Empty : this.RefererUri.ToString()));
            builder.AppendLine("Cookie " + this.CookieContainer.GetCookies(this.TargetUri).ToCookieRawString());

            return builder.ToString();
        }

        /// <summary>
        /// Initializes the by raw.
        /// Raw sample:
        /// <example>
        /// </example>
        /// </summary>
        /// <param name="fiddlerRequest">The fiddler request.</param>
        /// <param name="raw">The raw.</param>
        protected static void InitializeByRaw(FiddlerRequest fiddlerRequest, string raw)
        {
            if (fiddlerRequest != null && !string.IsNullOrWhiteSpace(raw))
            {
                string[] properties = raw.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (var property in properties)
                {
                    string[] keyValue = property.Split(new char[] { ' ' }, 2);

                    if (keyValue.Length >= 2)
                    {
                        FillFiddlerValue(fiddlerRequest, keyValue[0].Trim().TrimEnd(':'), keyValue[1].Trim());
                    }
                }
            }
        }

        /// <summary>
        /// Fills the fiddler value.
        /// </summary>
        /// <param name="fiddlerRequest">The fiddler request.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        protected static void FillFiddlerValue(FiddlerRequest fiddlerRequest, string key, string value)
        {
            if (fiddlerRequest != null && !string.IsNullOrWhiteSpace(key))
            {
                switch (key.ToLowerInvariant())
                {
                    case "get":
                    case "post":
                    case "delete":
                    case "put":
                        string[] urlParts = value.GetStringValue().Split(' ');
                        if (urlParts.Length >= 1)
                        {
                            fiddlerRequest.TargetUri = new Uri(urlParts[0]);
                        }

                        fiddlerRequest.Method = (MethodType)(Enum.Parse(typeof(MethodType), key, true));
                        break;
                    case "referer":
                        fiddlerRequest.RefererUri = string.IsNullOrWhiteSpace(value) ? null : new Uri(value);
                        break;
                    case "accept":
                        fiddlerRequest.Accept = value.GetStringValue();
                        break;
                    case "useragent":
                    case "user-agent":
                        fiddlerRequest.UserAgent = value.GetStringValue();
                        break;
                    case "cookie":
                        fiddlerRequest.CookieContainer.SetCookieByString(value, fiddlerRequest.TargetUri);
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Creates the specified fiddler request raw.
        /// </summary>
        /// <param name="fiddlerRequestRaw">The fiddler request raw.</param>
        /// <returns>FiddlerRequest.</returns>
        public static FiddlerRequest Create(string fiddlerRequestRaw)
        {
            return new FiddlerRequest(fiddlerRequestRaw);
        }
    }
}
